package com.visec.hdy.transmission.ftp.code;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import com.visec.hdy.transmission.beans.FileModel;
import com.visec.hdy.transmission.beans.FileModelHandle;
import com.visec.hdy.transmission.beans.FileModelProxy;
import com.visec.hdy.transmission.ftp.config.FtpModel;
import com.visec.hdy.transmission.ftp.utils.FtpFileTypeUtil;
import com.visec.hdy.utils.TimeUtil;

public class FtpClientImp implements FtpClientInter {

	private FtpModel ftpModel;

	private FTPClient ftpClient;

	public static FtpClientInter getInstance(FtpModel f) {
		FtpClientImp fci = new FtpClientImp();
		fci.ftpModel = f;
		return fci;
	}

	protected FtpClientImp() {
		// TODO Auto-generated constructor stub

	}

	@Override
	public List<FTPFile> listFiles(String path) {
		// TODO Auto-generated method stub
		testConnect();
		List<FTPFile> files = new ArrayList<FTPFile>();
		try {
			FTPFile[] listFiles = ftpClient.listFiles(path);
			for (FTPFile f : listFiles) {
				if (f.isFile())
					files.add(f);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return files;
	}

	private boolean testConnect() {
		if (ftpClient != null) {
			try {
				ftpClient.sendCommand("pwd");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				try {
					ftpClient.disconnect();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				return relogin();
			}
			return true;
		} else {
			return login();
		}
	}

	@Override
	public List<String> listDirs(String path) {
		// TODO Auto-generated method stub
		testConnect();
		List<String> dirs = new ArrayList<String>();
		try {
			FTPFile[] listFiles = ftpClient.listDirectories(path);
			for (FTPFile f : listFiles) {
				dirs.add(f.getName());
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return dirs;
	}

	@Override
	public boolean entryDir(String dir) {
		// TODO Auto-generated method stub
		try {
			testConnect();
			return ftpClient.changeWorkingDirectory(ftpCharset(dir));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public boolean uploadFile(String ftpPath, InputStream i, boolean isAdd) {
		// TODO Auto-generated method stub
		boolean isSuccess = false;
		try {
			if (isAdd) {
				isSuccess = ftpClient.appendFile(ftpCharset(ftpPath), i);
			} else {
				isSuccess = ftpClient.storeFile(ftpCharset(ftpPath), i);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return isSuccess;
	}

	@Override
	public OutputStream uploadFile(String ftpPath, boolean isAdd) {
		// TODO Auto-generated method stub
		try {
			if (isAdd)
				return ftpClient.appendFileStream(ftpCharset(ftpPath));
			else
				return ftpClient.storeFileStream(ftpCharset(ftpPath));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public InputStream downFile(String ftpPath) {
		// TODO Auto-generated method stub
		try {
			return ftpClient.retrieveFileStream(ftpCharset(ftpPath));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public boolean logout() {
		// TODO Auto-generated method stub
		if (ftpClient != null && ftpClient.isConnected()) {
			try {
				ftpClient.sendCommand("pwd");
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				ftpClient = null;
				return true;
			}
			try {
				ftpClient.logout();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				ftpClient.disconnect();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		ftpClient = null;
		return true;
	}

	@Override
	public boolean login() {
		// TODO Auto-generated method stub
		if (ftpClient != null && ftpClient.isConnected() && ftpClient.isAvailable()) {
			try {
				ftpClient.sendCommand("pwd");
				return true;
			} catch (IOException e) {
				// TODO Auto-generated catch block
			}
		}
		ftpClient = new FTPClient();
		try {
			ftpClient.connect(ftpModel.getIp(), ftpModel.getPort());
			boolean login = ftpClient.login(ftpModel.getUser(), ftpModel.getPassword());
			if (login) {
				ftpClient.setCharset(Charset.forName(ftpModel.getCharset()));
				ftpClient.setControlEncoding(ftpModel.getCharset());
				ftpClient.enterLocalPassiveMode();
			} else {
				ftpClient.disconnect();
			}
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return ftpClient.isConnected();
	}

	@Override
	public boolean deleteFile(String fileName) {
		// TODO Auto-generated method stub
		try {
			return ftpClient.deleteFile(ftpCharset(fileName));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public boolean deleteDir(String dir) {
		// TODO Auto-generated method stub
		try {
			return ftpClient.removeDirectory(ftpCharset(dir));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public String currentDir() {
		// TODO Auto-generated method stub
		try {
			String currdir = clientCharset(ftpClient.printWorkingDirectory());
			if (currdir.indexOf("\\") > -1)
				return currdir.replaceAll("\\", "/");
			else
				return currdir;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public FtpClientInter copy() {
		// TODO Auto-generated method stub
		return FtpClientImp.getInstance(ftpModel);
	}

	@Override
	public boolean createDir(String dir) {
		// TODO Auto-generated method stub
		testConnect();
		try {
			return ftpClient.makeDirectory(ftpCharset(dir));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public boolean isConnected() {
		// TODO Auto-generated method stub
		if (ftpClient == null)
			return false;
		return ftpClient.isConnected();
	}

	@Override
	public FTPFile getFile(String fileName) {
		// TODO Auto-generated method stub
		try {
			return ftpClient.mlistFile(ftpCharset(fileName));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public List<FileModel> listAllFiles() {
		// TODO Auto-generated method stub
		entryDir("/");
		List<FileModel> oldData = checkFtpFile(new ArrayList<FileModel>());
		return oldData;
	}

	private List<FileModel> checkFtpFile(List<FileModel> ftpFileMsgs) {
		String currentDir = currentDir();
		List<FTPFile> files = listFiles(currentDir);
		for (FTPFile f : files) {
			if (f.getSize() > 0) {
				ftpFileMsgs.add(new FileModel(TimeUtil.fomatDate(f.getTimestamp()), f.getName(), f.getSize(),
						currentDir(), FtpFileTypeUtil.fileTypeByName(f.getName())));
			}
		}
		List<String> listDirs = listDirs(currentDir);
		for (int i = 0; i < listDirs.size(); i++) {
			boolean entryDir = entryDir(currentDir + "/" + listDirs.get(i));
			if (entryDir) {
				checkFtpFile(ftpFileMsgs);
			}
		}
		return ftpFileMsgs;
	}

	private List<FileModelProxy> checkFtpFile(List<FileModelProxy> ftpFileMsgs, FileModelProxy fileModelProxy) {
		String currentDir = currentDir();
		List<FTPFile> files = listFiles(currentDir);
		for (FTPFile f : files) {
			if (f.getSize() > 0) {
				FileModelProxy clone = fileModelProxy.clone();
				clone.setFileModel(new FileModel(TimeUtil.fomatDate(f.getTimestamp()), f.getName(), f.getSize(),
						currentDir(), FtpFileTypeUtil.fileTypeByName(f.getName())));
				ftpFileMsgs.add(clone);
			}
		}
		List<String> listDirs = listDirs(currentDir);
		for (int i = 0; i < listDirs.size(); i++) {
			boolean entryDir = entryDir(currentDir + "/" + listDirs.get(i));
			if (entryDir) {
				checkFtpFile(ftpFileMsgs, fileModelProxy);
			}
		}
		return ftpFileMsgs;
	}

	@Override
	public boolean setFileType(int fileType) {
		// TODO Auto-generated method stub
		try {
			return ftpClient.setFileType(fileType);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	private String ftpCharset(String str) {
		try {
			return new String(str.getBytes(ftpModel.getCharset()), ftpModel.getFtpCharset());
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	private String clientCharset(String str) {
		try {
			return new String(str.getBytes(ftpModel.getFtpCharset()), ftpModel.getCharset());
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public List<FileModelProxy> listAllFiles(FileModelProxy fileModelProxy) {
		// TODO Auto-generated method stub
		entryDir("/");
		List<FileModelProxy> oldData = checkFtpFile(new ArrayList<FileModelProxy>(), fileModelProxy);
		return oldData;
	}

	@Override
	public void handleAllFiles(FileModelHandle handle) {
		// TODO Auto-generated method stub
		entryDir("/");
		if (handle.open()) {
			try {
				handleAllFile(handle);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			} finally {
				handle.close();
			}
		}
	}

	protected void handleAllFile(FileModelHandle handle) {
		String currentDir = currentDir();
		List<FTPFile> files = listFiles(currentDir);
		for (FTPFile f : files) {
			if (f.getSize() > 0) {
				boolean handleFile = handle.handleFile(new FileModel(TimeUtil.fomatDate(f.getTimestamp()), f.getName(),
						f.getSize(), currentDir, FtpFileTypeUtil.fileTypeByName(f.getName())));
				if (!handleFile) {
					return;
				}
			}
		}
		List<String> listDirs = listDirs(currentDir);
		for (int i = 0; i < listDirs.size(); i++) {
			boolean entryDir = entryDir(currentDir + "/" + listDirs.get(i));
			if (entryDir) {
				handleAllFile(handle);
			}
		}
	}

	@Override
	public boolean completePendingCommand() {
		// TODO Auto-generated method stub
		try {
			return ftpClient.completePendingCommand();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;
	}

	@Override
	public boolean relogin() {
		// TODO Auto-generated method stub
		logout();
		return login();
	}

}
